#include "decoder_encoder.h"
#include <iostream>
#include <sstream>
#include <string>
#include <iomanip>
namespace Media
{

	DecoderEncoder::DecoderEncoder(AVStream* in_video_stream,
		AVStream* in_audio_stream,
		enum AVCodecID video_codec,
		enum AVCodecID audio_codec) :p_in_video_stream(in_video_stream),
		p_in_audio_stream(in_audio_stream),
		m_video_out_codec(video_codec),
		m_audio_out_codec(audio_codec)
	{

	}

	//检查是否需要转码 
	bool DecoderEncoder::checkNeedTranslater(AVStream* stream, enum AVCodecID codec_id)
	{
		return stream->codecpar->codec_id != codec_id;
	}

	bool DecoderEncoder::createDecoderCodec(AVCodecContext** codec_context,
		AVStream* stream)
	{
		if (nullptr == stream)
		{
			Log::printError("输入的码流为空");
			return false;
		}


		//根据索引拿到对应的流
		const AVCodec* pCodec = avcodec_find_decoder(stream->codecpar->codec_id);
		if (!pCodec)
		{
			Log::printError("解码器打开失败");
			return false;
		}

		{
			//申请一个解码上下文
			*codec_context = avcodec_alloc_context3(pCodec);
			if (*codec_context == nullptr)
			{
				Log::printError("解码器申请内存失败");
				return false;
			}
		}


		//Log::printInfo(" decoder init audiochannel_layout init start" + std::to_string( stream->codecpar->channel_layout));

		//用流解码信息初始化编码参数
		if (avcodec_parameters_to_context(*codec_context, stream->codecpar) < 0)
		{
			Log::printError("拷贝解码器失败");
			avcodec_free_context(&*codec_context);
			*codec_context = nullptr;
			return false;
		}


		// 设置时间基准
		(* codec_context)->pkt_timebase = stream->time_base;

		if (stream->codecpar->channel_layout != av_get_default_channel_layout(stream->codecpar->channels))
		{
			(*codec_context)->channel_layout = av_get_default_channel_layout(stream->codecpar->channels);
			Log::printInfo("changge channel_layout" + std::to_string((*codec_context)->channel_layout));
		}
		
		//5.打开解码器
		if (avcodec_open2(*codec_context, pCodec, NULL) < 0)
		{
			Log::printError("编码器打开失败");
			avcodec_free_context(&*codec_context);
			*codec_context = nullptr;
			return false;
		}
		//Log::printInfo(" decoder init audiochannel_layout codec init end" + std::to_string((*codec_context)->channel_layout));
		return true;
	}

	// 是否需要音频转码
	bool DecoderEncoder::isTranslaterAudioCodec(AVCodecContext** p_codec_ctx)
	{
		*p_codec_ctx = p_audio_encoder_codec_ctx;
		return m_audio_translate;
	}

	// 是否需要视频转码
	bool DecoderEncoder::isTranslaterVideoCodec(AVCodecContext** p_codec_ctx)
	{
		*p_codec_ctx = p_video_encoder_codec_ctx;
		return m_video_translate;
	}

	std::string dec2hex(int i) //将int转成16进制字符串
	{
		std::stringstream ioss; //定义字符串流
		std::string s_temp; //存放转化后字符
		ioss << std::setiosflags(std::ios::uppercase) << std::hex << i; //以十六制(大写)形式输出
		//ioss << resetiosflags(ios::uppercase) << hex << i; //以十六制(小写)形式输出//取消大写的设置
		ioss >> s_temp;
		return s_temp;
	}



	bool  DecoderEncoder::init_audio_filters()
	{
		// 1、创建滤镜管道
		auto graph = avfilter_graph_alloc();

		// 2、添加输入滤镜;此滤镜是滤镜管道必须的滤镜，它作为滤镜管道的源头，用于接收从外部输入的数据
		const AVFilter* src_flt = avfilter_get_by_name("abuffer");
		if (!src_flt) {
			return false;
		}

		// 2.1 创建输入滤镜的上下文(输入滤镜实例)并添加到滤镜管道中;最后一个参数名随意起
		p_audio_src_flt_ctx = avfilter_graph_alloc_filter(graph, src_flt, "src_buffer");
		if (!p_audio_src_flt_ctx) {
			Log::printError("avfilter_graph_alloc_filter error");
			return false;
		}

		/** 2.2 设置输入滤镜的相关参数(采样率，采样格式，时间基，声道类型，声道数)并初始化,这里的参数要跟实际输入的音频数据参数保持一致;这是第一种初始化滤镜参数的方式
	*  av_opt_setxxx()系列函数最后一个参数的含义：
	*  0：只在对象的AVClass的option属性里面查找是否有对应的参数赋值，如果没有那么设置将无效；
	*  AV_OPT_SEARCH_CHILDREN：先在对象的AVClass的child_next指向的AVClass的option属性查找是否有对应的参数，然后在对象的AVClass的option属性查找对应参数
	*  AV_OPT_SEARCH_FAKE_OBJ：先在对象的AVClass的child_class_next指向的AVClass的option属性查找是否有对应的参数，然后在对象的AVClass的option属性查找对应参数
	*  遇到问题：avfilter_init_str()函数返回失败
	*  分析原因：调用av_opt_set_xx()系列函数给p_audio_src_flt_ctx设置参数时无效，因为之前最后一个参数传的为0，AVFilterContext的option属性是不含有这些参数的(它的option属性
	*  的child_next指向的AVClass的option属性才有这些参数)，所以最后一个参数应该为AV_OPT_SEARCH_CHILDREN
	*  解决方案：将最后一个参数设置为AV_OPT_SEARCH_CHILDREN
	*/
	// 在libavfilter/buffersrc.c文件中可以找到定义
		char ch_layout[64];
		av_get_channel_layout_string(ch_layout, sizeof(ch_layout), p_audio_decoder_codec_ctx->channels, 
			av_get_default_channel_layout(p_audio_decoder_codec_ctx->channels));
		av_opt_set(p_audio_src_flt_ctx, "channel_layout", ch_layout, AV_OPT_SEARCH_CHILDREN);
		av_opt_set_sample_fmt(p_audio_src_flt_ctx, "sample_fmt", (enum AVSampleFormat)p_audio_decoder_codec_ctx->sample_fmt, AV_OPT_SEARCH_CHILDREN);
		av_opt_set_q(p_audio_src_flt_ctx, "time_base", p_audio_decoder_codec_ctx->time_base, AV_OPT_SEARCH_CHILDREN);
		av_opt_set_int(p_audio_src_flt_ctx, "sample_rate", p_audio_decoder_codec_ctx->sample_rate, AV_OPT_SEARCH_CHILDREN);
		av_opt_set_int(p_audio_src_flt_ctx, "channels", p_audio_decoder_codec_ctx->channels, AV_OPT_SEARCH_CHILDREN);
		// 因为前面已经通过av_opt_set()函数给p_audio_src_flt_ctx设置了对应参数的值，所以这里初始化的时候不需要再传递任何参数了
		if (avfilter_init_dict(p_audio_src_flt_ctx, NULL) < 0) {    // 这里换成avfilter_init_str()函数也是可以的
			
			Log::printError(ch_layout);
			Log::printError("avfilter_init_dict error");
			return false;
		}

		// 4、创建格式转化滤镜;
		const AVFilter* aformat = avfilter_get_by_name("aformat");
		if (!aformat) {
			return false;
		}
		// 4.1创建格式转换的上下文
		AVFilterContext* aformat_ctx = avfilter_graph_alloc_filter(graph, aformat, "format");
		if (!aformat_ctx) {
			return false;
		}
		// 4.2 设置格式转换滤镜参数并初始化;具体参数的名字见libavfilter/af_aformat.c文件的aformat_options变量
		// 这是第三种设置滤镜参数的方式，采用key1=value1:key2=value2....的字符串形式组织各个参数的值，avfilter_init_str()函数内部会自己解析
		std::string  format_opts = "sample_fmts="
			+ std::string(av_get_sample_fmt_name(p_audio_encoder_codec_ctx->sample_fmt))
			+":sample_rates="+std::to_string(p_audio_encoder_codec_ctx->sample_rate) 
			+ ":channel_layouts=0x"+ std::to_string((uint64_t)p_audio_encoder_codec_ctx->channel_layout);
		Log::printInfo("audio format_opts" + format_opts);
		if (avfilter_init_str(aformat_ctx, format_opts.data()) < 0) {
			return false;
		}

		// 5、创建输出滤镜
		const AVFilter* sink = avfilter_get_by_name("abuffersink");
		if (!sink) {
			return false;
		}
		// 5.1 创建输出滤镜的上下文
		p_audio_sink_flt_ctx = avfilter_graph_alloc_filter(graph, sink, "sink");
		if (!p_audio_sink_flt_ctx) {
			return false;
		}

		// 5.2 初始化输出滤镜;由于输出滤镜是接受最后一个滤镜的数据，只是做一个中转，所以不需要设置任何参数
		if (avfilter_init_str(p_audio_sink_flt_ctx, NULL) < 0) {
			return false;
		}

		/** 6、将各个滤镜连接起来(第一个滤镜一定是输入滤镜，最后一个滤镜一定是输出滤镜)
		 *  avfilter_link()中代表将第一个参数对应滤镜输出端口(端口号为srcpad，一般都是0)连接到第三个参数对应滤镜的输入端口(端口号为dstpad,一般都是0)
		 *  如下代最终滤镜的连接顺序为：
		 *  srcbuffer->volume->aformat->sink
		 */
		int ret = avfilter_link(p_audio_src_flt_ctx, 0, aformat_ctx, 0);
		if (ret >= 0) {
			ret = avfilter_link(aformat_ctx, 0, p_audio_sink_flt_ctx, 0);
		}
		if (ret < 0) {
			return false;
		}

		// 7、配置滤镜管道(也即初始化滤镜管道)；只有初始化滤镜管道后才可以使用各个滤镜进行音频处理;第二个参数用来打印日志的上下文，设为NULL即可
		if (avfilter_graph_config(graph, NULL) < 0) {
			return false;
		}

		/** 遇到问题：重新编码时提示"[libmp3lame @ 0x10380b800] more samples than frame size (avcodec_encode_audio2)"
		 *  分析原因：因为编码器上下文中设置的frame_size的大小为1152，而通过滤镜管道的av_buffersink_get_frame()函数获取的AVFrame的nb_samples的大小为1254 >=1152
		 *  解决方案：通过av_buffersink_set_frame_size()给输出滤镜设置输出固定的AVFrame的nb_samples的大小
		 *
		 *  分析：av_buffersink_set_frame_size()函数内部实际上是将AVFilterLink的min_samples，max_samples，partial_buf_size都设置为了这个值。那么当调用av_buffersink_get_frame()函数时
		 *  每次输出的AVFrame大小将为这个值
		 *  备注：av_buffersink_set_frame_size()的调用必须再avfilter_link()连接之后，否则会崩溃
		 */

		
		av_buffersink_set_frame_size(p_audio_sink_flt_ctx, p_audio_encoder_codec_ctx->frame_size);
		



		return true;
	}


	// 生成编码器
	bool DecoderEncoder::createEncoderCodec(AVStream *stream,
		AVCodecContext** codec_context, AVCodecContext* decoder_codec, enum AVCodecID codec_id)
	{
		if( nullptr == decoder_codec)
		{
			Log::printError("createEncoderCodec - 输入编码器为空");
			return false;
		}
		// 源流编码器
		auto original_codec = decoder_codec;
		AVMediaType media_type = stream->codecpar->codec_type;

		// 1.找到编码器
		AVCodec* codec = nullptr;
		codec = (AVCodec*)avcodec_find_encoder(codec_id);
		if (!codec)
		{
			Log::printError("createEncoderCodec - 未找到编码器");
			return false;
		}


		//申请一个解码上下文
		(* codec_context) = avcodec_alloc_context3(codec);
		if ((*codec_context) == nullptr)
		{
			Log::printError("编码器申请内存失败");
			return false;
		}


		switch (media_type)
		{
			// 1.初始化视频编码器
		case AVMEDIA_TYPE_VIDEO: {
			
			// 设置视频相关参数
			// 视频码率设置
			(*codec_context)->bit_rate = 400000;
			// 分辨率设置
			(*codec_context)->width = original_codec->width;
			(*codec_context)->height = original_codec->height;
			

			AVRational v1;
			v1.num = 1;
			v1.den = stream->r_frame_rate.num;
			(*codec_context)->time_base = v1;

			AVRational v2;
			v2.num = stream->r_frame_rate.num;
			v2.den = 1;
			(*codec_context)->framerate = v2;
			// 时间基设置
			//(*codec_context)->time_base = stream->time_base;
			//(*codec_context)->framerate = stream->r_frame_rate;

			/* emit one intra frame every ten frames
			* check frame pict_type before passing frame
			* to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
			* then gop_size is ignored and the output of encoder
			* will always be I frame irrespective to gop_size
			*/
			//codec_context->gop_size = 10;
			(*codec_context)->max_b_frames = 0;
			(*codec_context)->pix_fmt = (AVPixelFormat)stream->codecpar->format;

		}; break;
			// 2.初始化音频编码器
		case AVMEDIA_TYPE_AUDIO: {
			// 设置音频相关参数 aac统一编fltp
			(*codec_context)->sample_fmt = AV_SAMPLE_FMT_FLTP;
			(*codec_context)->bit_rate = original_codec->bit_rate;
			(*codec_context)->sample_rate = original_codec->sample_rate;
			AVRational a1 = { 1, original_codec->sample_rate };
			(*codec_context)->time_base = a1;
			(*codec_context)->channels = original_codec->channels;
			(*codec_context)->channel_layout = original_codec->channel_layout;
			//(*codec_context)->channel_layout = original_codec->channel_layout;
			(*codec_context)->frame_size = original_codec->frame_size;
			
			Log::printInfo("audio channel_layout" + std::to_string((*codec_context)->channel_layout));
		}; break;
		default:
			return false;
			break;
		}

		
		// 打开编码器
		if (avcodec_open2((*codec_context), codec, nullptr) < 0)
		{
			Log::printError("打开视频编码器失败");
			return false;
		}

		if (media_type == AVMEDIA_TYPE_AUDIO)
		{
			Log::printInfo("init audio translater start");
			// 初始化音频缓冲队列
			p_audio_fifo = av_audio_fifo_alloc((*codec_context)->sample_fmt,
				(*codec_context)->channels,
				(*codec_context)->frame_size);
			if (p_audio_fifo == nullptr)
			{
				Log::printError("初始化音频缓冲队列失败");
				return false;
			}

			Log::printInfo("init audio filter start");

			// 初始化滤镜
			this->init_audio_filters();
			Log::printInfo("init audio translater end");
		}
		
		return true;
	}

	// 送入一帧,输出好几帧
	bool DecoderEncoder::addPacket( const enum AVMediaType&type,
		const AVPacket* packet,
		std::queue< std::shared_ptr<AVPacket> >& packet_out)
	{
		AVCodecContext* decoder_codec_ctx = nullptr;
		switch (type)
		{
		case AVMEDIA_TYPE_VIDEO: {
			decoder_codec_ctx = p_video_decoder_codec_ctx;
		}; break;
		case AVMEDIA_TYPE_AUDIO: {
			decoder_codec_ctx = p_audio_decoder_codec_ctx;
		}; break;
		default:
			return false;
			break;
		}

		//编码一帧
		auto ret = avcodec_send_packet(decoder_codec_ctx, packet);
		if (ret < 0)
		{
			Log::printError("Decode error");
			return true;
		}

		while (ret >= 0)
		{
			// 申请yuv/pcm一帧
			std::shared_ptr<AVFrame> frame(av_frame_alloc(), [](AVFrame* frame) {
				av_frame_free(&frame);
				});

			ret = avcodec_receive_frame(decoder_codec_ctx, frame.get());

			if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
			{
				return true;
			}
			else if (ret < 0)
			{
				Log::printError("解码一帧失败");
				return false;
			}

			
			switch (type)
			{
			case AVMEDIA_TYPE_VIDEO: {
				bool ret_encoder = this->encoderFrame(type, frame, packet_out);
				if (!ret_encoder)
				{
					return false;
				}
			}; break;
			case AVMEDIA_TYPE_AUDIO: {
				bool ret_encoder = this->filterFrame(type, frame, packet_out);
				if (!ret_encoder)
				{
					return false;
				}
			}; break;
			default:
				return false;
				break;
			}

			

		}
		return true;
	}

	// 解码出来的一帧数据过滤镜
	bool DecoderEncoder::filterFrame(const enum AVMediaType& type, 
		const std::shared_ptr<AVFrame>& frame,
		std::queue< std::shared_ptr<AVPacket> >& packet_out)
	{
		// 解码成功；利用音频滤镜进行转换
		int ret = av_buffersrc_add_frame_flags(p_audio_src_flt_ctx, frame.get(), AV_BUFFERSRC_FLAG_PUSH);
		if (ret < 0) {
			
			return false;
		}

		while (ret >= 0) {

			// 申请yuv/pcm一帧
			std::shared_ptr<AVFrame> frame(av_frame_alloc(), [](AVFrame* frame) {
				av_frame_free(&frame);
				});

			ret = av_buffersink_get_frame_flags(p_audio_sink_flt_ctx, frame.get(), AV_BUFFERSINK_FLAG_NO_REQUEST);
			if (ret == AVERROR_EOF) {   // 说明结束了
				break;
			}
			else if (ret < 0) {
				break;
			}

			bool ret_encoder = this->encoderFrame(type, frame, packet_out);
			if (!ret_encoder)
			{
				return false;
			}
			
		}
		return  true;
	}

	// 解码一帧
	bool DecoderEncoder::encoderFrame(const enum AVMediaType& type,
		const std::shared_ptr<AVFrame>& frame,
		std::queue< std::shared_ptr<AVPacket> >& packet_out)
	{
		int ret = -1;
		AVCodecContext* encoder_codec_ctx = nullptr;

		std::queue< std::shared_ptr<AVFrame> > video_audio_frame_queue;
		switch (type)
		{
		case AVMEDIA_TYPE_VIDEO: {
			encoder_codec_ctx = p_video_encoder_codec_ctx;
			frame->pts = m_video_sample_count++;
			video_audio_frame_queue.push(frame);
		}; break;
		case AVMEDIA_TYPE_AUDIO: {

			//解码音频
			encoder_codec_ctx = p_audio_encoder_codec_ctx;

			// fifo相关
			int error;
			/* Make the FIFO as large as it needs to be to hold both,
			 * the old and the new samples. */
			if ((error = av_audio_fifo_realloc(p_audio_fifo, av_audio_fifo_size(p_audio_fifo) + frame->nb_samples)) < 0) {
				av_log(NULL, AV_LOG_ERROR, "Could not reallocate FIFO\n");
				return false;
			}

			/* Store the new samples in the FIFO buffer. */
			if (av_audio_fifo_write(p_audio_fifo, (void**)frame->data,
				frame->nb_samples) < frame->nb_samples) {
				av_log(NULL, AV_LOG_ERROR, "Could not write data to FIFO\n");
				break;
			}


			int audio_fifo_size = av_audio_fifo_size(p_audio_fifo);
			if (audio_fifo_size < encoder_codec_ctx->frame_size) {
				/* Decode one frame worth of audio samples, convert it to the
				 * output sample format and put it into the FIFO buffer. */
				return false;
			}



			while (av_audio_fifo_size(p_audio_fifo) >= encoder_codec_ctx->frame_size)
			{
				const int frame_size = FFMIN(av_audio_fifo_size(p_audio_fifo), encoder_codec_ctx->frame_size);

				std::shared_ptr<AVFrame> output_frame(av_frame_alloc(), [](AVFrame* frame) {
					av_frame_free(&frame);
					});

				output_frame->format = encoder_codec_ctx->sample_fmt;
				output_frame->channel_layout = encoder_codec_ctx->channel_layout;
				output_frame->sample_rate = encoder_codec_ctx->sample_rate;
				output_frame->nb_samples = encoder_codec_ctx->frame_size;
				output_frame->channels = encoder_codec_ctx->channels;
				output_frame->channel_layout = encoder_codec_ctx->channel_layout;
				if (av_frame_get_buffer(output_frame.get(), 0) < 0)
				{

					break;
				}

				if (av_audio_fifo_read(p_audio_fifo, (void**)output_frame->data, output_frame->nb_samples) < frame_size) {
					av_log(NULL, AV_LOG_ERROR, "Could not read data from FIFO\n");
					return false;
				}


				AVRational a1 = { 1, encoder_codec_ctx->sample_rate };
				output_frame->pts = av_rescale_q(m_audio_sample_count, a1, encoder_codec_ctx->time_base);;
				m_audio_sample_count += output_frame->nb_samples;


				video_audio_frame_queue.push(output_frame);
			}

		}; break;
		default:
			Log::printError("无法确定es流编码类型，无法解码");
			break;
		}
		// 没找到es编码器，不做处理
		if (encoder_codec_ctx == nullptr)
		{
			return false;
		}

		while (!video_audio_frame_queue.empty())
		{
			auto av_frame = video_audio_frame_queue.front();
			video_audio_frame_queue.pop();

			ret = avcodec_send_frame(encoder_codec_ctx, av_frame.get());
			if (ret < 0) {
				Log::printError("Error sending a frame for encoding");
				return true;
			}



			while (ret >= 0)
			{

				//编码数据
				std::shared_ptr<AVPacket> packet(av_packet_alloc(), [](AVPacket* packet) {
					av_packet_free(&packet);
					});
				ret = avcodec_receive_packet(encoder_codec_ctx, packet.get());
				if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
				{
					break;
				}
				else if (ret < 0)
				{
					Log::printError("编码一帧失败");
					return false;
				}

				// 编码成功压入一帧
				packet_out.push(packet);


			}

		}
		return true;
	}


	bool DecoderEncoder::start()
	{
		// 如果视频或者音频stream为空返回失败
		if (p_in_video_stream == nullptr || p_in_audio_stream == nullptr)
		{
			return false;
		}

		// 1.校验输入音视频是否需要转码
		if (this->checkNeedTranslater(p_in_video_stream, m_video_out_codec))
		{
			// 2.若视频需要转码生成解码器
			if (!this->createDecoderCodec(&p_video_decoder_codec_ctx, p_in_video_stream))
			{
				return false;
			}

			// 3.打开视频编码器
			if (!this->createEncoderCodec(p_in_video_stream ,&p_video_encoder_codec_ctx, p_video_decoder_codec_ctx, m_video_out_codec))
			{
				return false;
			}
			// 视频需要转换
			m_video_translate = true;

		}

		if (this->checkNeedTranslater(p_in_audio_stream, m_audio_out_codec))
		{
			// 2.若音频需要转码生成解码器和编码器
			if (!this->createDecoderCodec(&p_audio_decoder_codec_ctx, p_in_audio_stream))
			{
				return false;
			}

			// 3.打开音频编码器
			if (!this->createEncoderCodec(p_in_audio_stream ,&p_audio_encoder_codec_ctx, p_audio_decoder_codec_ctx, m_audio_out_codec))
			{
				return false;
			}
			// 音频需要转换
			m_audio_translate = true;
		}

		return true;

	}

	DecoderEncoder::~DecoderEncoder()
	{
		if (p_audio_src_flt_ctx != nullptr)
		{
			avfilter_free(p_audio_src_flt_ctx);
			p_audio_src_flt_ctx = nullptr;
		}

		if (p_audio_sink_flt_ctx != nullptr)
		{
			avfilter_free(p_audio_sink_flt_ctx);
			p_audio_sink_flt_ctx = nullptr;
		}
		

		// 释放视频解码器
		if(p_video_decoder_codec_ctx != nullptr)
		{
			avcodec_free_context(&p_video_decoder_codec_ctx);
			p_video_decoder_codec_ctx = nullptr;
		}

		// 释放音频解码器
		if (p_audio_decoder_codec_ctx != nullptr)
		{
			avcodec_free_context(&p_audio_decoder_codec_ctx);
			p_audio_decoder_codec_ctx = nullptr;
		}


		// 释放视频编码器
		if (p_video_encoder_codec_ctx != nullptr)
		{
			avcodec_free_context(&p_video_encoder_codec_ctx);
			p_video_encoder_codec_ctx = nullptr;
		}

		//  释放音频编码器
		if (p_audio_encoder_codec_ctx != nullptr)
		{
			avcodec_free_context(&p_audio_encoder_codec_ctx);
			p_audio_encoder_codec_ctx = nullptr;
		}

	}



}
